#include "InstructionTable.h"
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;

bool InstructionTable::IsJumpStackEmpty(){
	return this->jumpStack.empty();
}

string ConvertInstructionType(Instruction_t iType);
InstructionTable::InstructionTable()
{
	instructionAddress = 0;
}

void InstructionTable::BackPatch(int jumpAddress)
{
	int address = jumpStack.top();
	jumpStack.pop();
	instructions[address].Operand = jumpAddress;
}

int InstructionTable::GenerateInstruction(const Instruction_t & instruction, int symbolAddress)
{
	Instruction ninstruction;
	ninstruction.Operand = symbolAddress;
	ninstruction.InstructionType = instruction;
	ninstruction.Address = instructionAddress;
	cout<<"Generating Instruction:\t"<<"Address: "<< ninstruction.Address<<" Instruction Type: "<<ConvertInstructionType(ninstruction.InstructionType)<<" Operand: "<< ninstruction.Operand<< endl;
	instructionAddress++;
	instructions.push_back(ninstruction);
	return instructionAddress - 1;
}

void InstructionTable::PushJumpStack(int instructionAddress)
{
	jumpStack.push(instructionAddress);
}

void InstructionTable::PushJumpStack()
{
	jumpStack.push(instructionAddress);
}

const int InstructionTable::CurrentAddress() const
{
	return this->instructionAddress;
}

void InstructionTable::GenerateInstruction(const Instruction_t &  instruction)
{
	int i = GenerateInstruction(instruction, -999);
}

void InstructionTable::PrintTable(const string& fileName)
{
    ofstream outFile;
    outFile.open(fileName, ofstream::out|ofstream::app);
    if(outFile.is_open())
    {
		outFile<< "-------Instruction Table--------\n"<<endl;
		outFile<< "Address"<<"\t\t"<< "Operation" <<"\t\t"<< "Operand"<< endl;
		for(int i = 0 ; i < instructionAddress; i++)
		{
			outFile<< instructions[i].Address<<"\t\t\t"<<ConvertInstructionType(instructions[i].InstructionType)<<"\t\t"<<instructions[i].Operand<< endl;
		}
		
    }
    outFile.close();
}

string ConvertInstructionType(Instruction_t iType)
{
	switch (iType)
	{
	case i_pushi:
		return "PUSHI";
	case i_pushm:
		return "PUSHM";
		break;
	case i_popm:
		return "POPM";
	case i_stdout:
		return "STDOUT";
	case i_stdin:
		return "STDIN";
	case i_add:
		return "ADD";
	case i_sub:
		return "SUB";
	case i_mul:
		return "MUL";
	case i_div:
		return "DIV";
	case i_grt:
		return "GRT";
	case i_les:
		return "LES";
	case i_equ:
		return "EQU";
	case i_jumpz:
		return "JUMPZ";
	case i_jump:
		return "JUMP";
	case i_label:
		return "LABEL";
	case i_length:
		return "LENGTH";
	}
}
